package fr.inserm.umr915.libjson;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.ObjectInputStream.GetField;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.w3c.dom.Document;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

public abstract class ObjectNode extends Node
	{
	static final String TAG="object";
	static final String PROPERTY="property";
	static final String PROP_NAME="name";
	private Map<String,Node> map=null;
	public ObjectNode()
		{
		map=new LinkedHashMap<String,Node>();
		}
	
	public ObjectNode(int capacity)
		{
		map=new LinkedHashMap<String,Node>(capacity);
		}
	
	public Map<String,Node> getMap()
		{
		return this.map;
		}
	@Override
	public NodeType getNodeType()
		{
		return NodeType.OBJECT;
		}
	
	@Override
	public int hashCode() {
		return getMap().hashCode();
		}
	
	public int size()
		{
		return getMap().size();
		}
	
	public boolean containsKey(String key)
		{
		return getMap().containsKey(key);
		}
	
	
	public ObjectNode castAndPut(String key,String value)
		{
		return put(key, getNodeFactory().cast(value));
		}
	
	public ObjectNode put(String key,String value)
		{
		return put(key, getNodeFactory().createStringNode(value));
		}
	
	public ObjectNode put(String key,int value)
		{
		return put(key, getNodeFactory().createIntNode(value));
		}
	
	public ObjectNode put(String key,double value)
		{
		return put(key, getNodeFactory().createFloatNode(value));
		}
	
	public ObjectNode put(String key,boolean value)
		{
		return put(key, getNodeFactory().createBooleanNode(value));
		}
	
	public ObjectNode put(String key,Node value)
		{
		getMap().put(key,value);
		return this;
		}
	
	public Node get(String key)
		{
		return getMap().get(key);
		}
	
	public ObjectNode getObject(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isObject()) throw new IllegalArgumentException(""+key+" is not an object but a "+n.getNodeType());
		return n.asObjectNode();
		}
	
	public ArrayNode getArray(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isArray()) throw new IllegalArgumentException(""+key+" is not an array but a "+n.getNodeType());
		return n.asArrayNode();
		}
	
	public ArrayNode getArrayOrCreate(String key)
		{
		ArrayNode a=getArray(key);
		if(a==null)
			{
			a=getNodeFactory().createArrayNode();
			put(key, a);
			}
		return a;
		}
	
	public String getString(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isString()) throw new IllegalArgumentException(""+key+" is not an string but a "+n.getNodeType());
		return n.asStringNode().getValue();
		}
	
	public Integer getInt(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isInteger()) throw new IllegalArgumentException(""+key+" is not an integer but a "+n.getNodeType());
		return n.asIntNode().getValue();
		}
	
	public Double getDouble(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isFloat()) throw new IllegalArgumentException(""+key+" is not an float but a "+n.getNodeType());
		return n.asFloatNode().getValue();
		}
	
	public Boolean getBoolean(String key)
		{
		Node n= get(key);
		if(n==null) return null;
		if(!n.isFloat()) throw new IllegalArgumentException(""+key+" is not an bool but a "+n.getNodeType());
		return n.asBooleanNode().getValue();
		}
	
	@Override
	public Node clone(NodeFactory owner)
		{
		ObjectNode cp= owner.createObjectNode(size());
		for(String key:getMap().keySet())
			{
			cp.getMap().put(key, this.getMap().get(key));
			}
		return cp;
		}
	
		
	@Override
	public void write(DataOutputStream out) throws IOException
		{
		out.writeByte((byte)getNodeType().ordinal());
		out.writeInt(getMap().size());
		for(String key: getMap().keySet())
			{
			out.writeUTF(key);
			getMap().get(key).write(out);
			}
		}	
	
	public Set<String> keySet()
		{
		return this.getMap().keySet();
		}
	
	@Override
	public void print(PrintWriter out)
		{
		boolean first=true;
		out.print('{');
		for(String key:keySet())
			{
			if(!first) out.print(',');
			first=false;
			if(key.matches("[a-zA-Z_][a-zA-Z_0-9]*") && !(key.equals("true") || key.equals("false") || key.equals("null")))
				{
				out.print(key);
				}
			else
				{
				StringNode.escape(out, key);
				}
			out.print(':');
			getMap().get(key).print(out);
			}
		out.print('}');
		}
	
	@Override
	public org.w3c.dom.Element asDom(Document owner)
		{
		org.w3c.dom.Element root= owner.createElement(TAG);
		for(String key:keySet())
			{
			org.w3c.dom.Element p= owner.createElement(PROPERTY);
			p.setAttribute(PROP_NAME, key);
			root.appendChild(p);
			p.appendChild(getMap().get(key).asDom(owner));
			}
		return root;
		}
	
	@Override
	protected void saxHandler(ContentHandler handler) throws SAXException,
			IOException
		{
		AttributesImpl atts=new AttributesImpl();
		atts.addAttribute("", "", "size", "CDATA", String.valueOf(size()));
		handler.startElement("", "",TAG, atts);
		for(String key: keySet())
			{
			atts.clear();
			atts.addAttribute("", "", PROP_NAME, "CDATA", key);
			handler.startElement("", "", PROPERTY, atts);
			getMap().get(key).saxHandler(handler);
			handler.endElement("", "", PROPERTY);
			}
		handler.endElement("", "",TAG);
		}
	
	
	@Override
	public void write(XMLStreamWriter out) throws XMLStreamException
		{
		out.writeStartElement(TAG);
		for(String key:keySet())
			{
			out.writeStartElement(PROPERTY);
			out.writeAttribute(PROP_NAME, key);
			getMap().get(key).write(out);
			out.writeEndElement();
			}
		out.writeEndElement();
		}
	
	
	@Override
	public String toString()
		{
		return getMap().toString();
		}
	}
